package org.harsh.dispatch;

import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.date.DateTime;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.harsh.common.TransDetail;
import org.harsh.transmission.domain.TransTask;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.PriorityBlockingQueue;

@Slf4j
public class CommVars {
    // 任务编号及对应的任务详情
    public static Map<Long, TransDetail> loadedTask = new ConcurrentHashMap<>();
    // 地址到节点, addr - nodeId
    public static Map<String, Long> addr2Node = new ConcurrentHashMap<>();
    // 任务优先级队列, nodeId - task Queue
    public static Map<Long, PriorityBlockingQueue<TransTask>> nodeTaskQueue = new ConcurrentHashMap<>();
    // 节点传输通道, NodeId - TcpChannel
    public static Map<Long, Channel> nodeChannel = new ConcurrentHashMap<>();
    // 节点KCP传输通道, NodeId - KcpChannel
    public static Map<Long, Channel> nodeKcpChannel = new ConcurrentHashMap<>();
    // 线程池
    public static final ExecutorService executorService = Executors.newCachedThreadPool();
    public static EventLoopGroup bossGroup = new NioEventLoopGroup(2);
    public static EventLoopGroup workGroup = new NioEventLoopGroup();

    /**
     * 插入装载任务信息
     * @param transTask
     */
    public synchronized static void insertLoadedTask(TransTask transTask) {
        if(!loadedTask.containsKey(transTask.getTaskId())) {
            TransDetail transDetail = new TransDetail();
            transDetail.setTransTask(transTask);
            transDetail.setLoadTime(DateTime.now());
            transDetail.setSendPos(0L);
            loadedTask.put(transTask.getTaskId(), transDetail);
        }
    }

    /**
     * 更新开始时间
     * @param transTask
     */
    public synchronized static void updateTaskStartTime(TransTask transTask, long totalSize){
        TransDetail transDetail = loadedTask.get(transTask.getTaskId());
        if(transDetail != null) {
            transDetail.setStartTime(DateTime.now());
            transDetail.setTotalSize(totalSize);
            loadedTask.put(transTask.getTaskId(), transDetail);
        }
    }

    /**
     * 更新发送偏移量
     * @param transTask
     * @param sendOffset
     */
    public synchronized static void updateTaskSendOffset(TransTask transTask, long sendOffset ){
        TransDetail transDetail = loadedTask.get(transTask.getTaskId());
        if(transDetail != null) {
            transDetail.setSendPos(sendOffset);
            loadedTask.put(transTask.getTaskId(), transDetail);
        }
    }

    /**
     * 更新发送回执长度
     * @param transTask
     * @param recvOffset
     */
    public synchronized static void updateTaskRecvOffset(TransTask transTask, long recvOffset){
        TransDetail transDetail = loadedTask.get(transTask.getTaskId());
        if(transDetail != null) {
            transDetail.getTransTask().setTaskOffset(recvOffset);
            loadedTask.put(transTask.getTaskId(), transDetail);
        }
    }

    // 从队列中获取任务
    public static TransTask takeTask(long nodeId) {
        TransTask task = null;
        if(nodeTaskQueue.containsKey(nodeId)) {
            try {
                PriorityBlockingQueue<TransTask> taskQueue = nodeTaskQueue.get(nodeId);
                if(taskQueue == null || taskQueue.size() ==0) {
                    return null;
                }
                task = taskQueue.take();
            } catch (InterruptedException e) {
                log.error("Dispatcher take task error, msg:{}", e.getMessage());
                e.printStackTrace();
            }
        }
        return task;
    }

    /**
     * 通道解密失败后，清除channel
     * @param nodeId
     */
    public synchronized static void removeChannel(long nodeId){
        nodeChannel.remove(nodeId);
        nodeKcpChannel.remove(nodeId);
    }

    /**
     * 通道解密失败后，清除channel
     * @param peerAddr
     */
    public synchronized static void removeChannel(String peerAddr){
        if(addr2Node.containsKey(peerAddr))
        {
            long nodeId = addr2Node.get(peerAddr);
            removeChannel(nodeId);
        }
    }
    /**
     * 同步判断节点是否已建立连接
     * @param nodeId
     * @return
     */
    public synchronized static boolean containNodeChannel(long nodeId) {
        return nodeChannel.containsKey(nodeId);
    }

    /**
     * 获取channel
     * @param nodeId
     * @param type
     * @return
     */
    public synchronized static Channel getChannel(long nodeId, boolean type){
        if(type) {
            return nodeChannel.get(nodeId);
        } else {
            return nodeKcpChannel.get(nodeId);
        }
    }

    /**
     * 获取已建立连接的传输节点
     * @return
     */
    public synchronized static Set<Long> getNode(){
        return nodeChannel.keySet();
    }

    /**
     * 根据传输节点获取任务
     * @param nodeId
     * @return
     */
    public synchronized static PriorityBlockingQueue<TransTask> getNodeTask(long nodeId){
        return nodeTaskQueue.getOrDefault(nodeId, null);
    }
}
